package com.minio.miniospringboot.service.impl;

import com.minio.miniospringboot.common.Constants;
import com.minio.miniospringboot.config.MinioProperties;
import com.minio.miniospringboot.entity.UploadResult;
import com.minio.miniospringboot.service.MinioService;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDate;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class MinioServiceImpl implements MinioService {

    @Resource
    private MinioProperties minioPro;

    @Resource
    private MinioClient minioClient;


    /**
     * 复制文件
     *
     * @param source
     * @param target
     */
    public ObjectWriteResponse copy(String source, String target) {
        try {
            String bucket = minioPro.getBucket();
            return minioClient.copyObject(CopyObjectArgs.builder()
                    .bucket(bucket)
                    .object(target)
                    .source(CopySource.builder()
                            .bucket(bucket)
                            .object(source)
                            .build())
                    .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除文件
     *
     * @param object
     */
    public void delete(String object) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioPro.getBucket())
                    .object(object)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取访问网关
     *
     * @param path
     * @return
     */
    protected String gateway(String path) {
        String gateway = minioPro.getGateway();
        if (!gateway.endsWith(Constants.URI_DELIMITER)) {
            gateway += Constants.URI_DELIMITER;
        }
        return gateway + path;
    }

    /**
     * 获取文件后缀
     *
     * @param fileName
     * @return
     */
    protected static String getSuffix(String fileName) {
        int index = fileName.lastIndexOf(".");
        if (index != -1) {
            String suffix = fileName.substring(index + 1);
            if (!suffix.isEmpty()) {
                return suffix;
            }
        }
        throw new IllegalArgumentException("非法文件名称：" + fileName);
    }

    /**
     * 获取年月日[2020, 09, 01]
     *
     * @return
     */
    protected static String[] getDateFolder() {
        String[] retVal = new String[3];
        LocalDate localDate = LocalDate.now();
        retVal[0] = localDate.getYear() + "";
        int month = localDate.getMonthValue();
        retVal[1] = month < 10 ? "0" + month : month + "";
        int day = localDate.getDayOfMonth();
        retVal[2] = day < 10 ? "0" + day : day + "";
        return retVal;
    }

    @Override
    public StatObjectResponse statObject(String fileName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.statObject(
                StatObjectArgs.builder().bucket(minioPro.getBucket()).object(fileName).build());
    }

    /**
     * 获取bucket列表
     *
     * @return
     */
    @Override
    public Iterable<Result<Item>> listObjects() {
        return minioClient.listObjects(
                ListObjectsArgs.builder().bucket(minioPro.getBucket()).build());
    }

    /**
     * base64编码的图片上传
     */
    @Override
    public UploadResult upload(MultipartFile multipartFile, boolean rename) {
        //oss是前端传来的base64对象，第一行代码将获取对象中的base64字符串
        try {
            String bucket = minioPro.getBucket();
            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucket).build();
            boolean bucketExists = minioClient.bucketExists(bucketExistsArgs);
            if (!bucketExists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(bucket)
                                .build());
                setBucketPolicy(bucket, minioPro.getBucketPolicy().replaceAll("\\{bucket\\}", bucket));
            }
            String originalFilename = multipartFile.getOriginalFilename();
            String finalPath = originalFilename;
            if (rename) {
                // UUID重命名
                String fileName = UUID.randomUUID().toString().replace("-", "") + "." + getSuffix(originalFilename);
                String[] folders = getDateFolder();
                // 年/月/日/file
                finalPath = String.join(Constants.URI_DELIMITER, folders) +
                        Constants.URI_DELIMITER +
                        fileName;
            }

            minioClient.putObject(PutObjectArgs.builder()
                    .stream(multipartFile.getInputStream(), multipartFile.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE)
                    .object(finalPath)
                    .contentType(multipartFile.getContentType())
                    .bucket(minioPro.getBucket())
                    .build());
            return new UploadResult(gateway(finalPath), bucket, finalPath);
        } catch (Exception e) {
            log.error("上传文件失败", e);
            throw new RuntimeException("上传文件失败", e);
        }
    }


    /**
     * 获取文件url预览
     */
    @Override
    public String getFileUrl(String objectName) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(minioPro.getBucket())
                    .method(Method.GET)
                    .expiry(2, TimeUnit.HOURS)
                    .object(objectName)
                    .build());
        } catch (Exception e) {
            log.error("获取文件url预览", e);
            throw new RuntimeException(e);
        }


    }

    /**
     * 创建bucket
     *
     * @param bucketName
     * @return
     */
    @Override
    public boolean createBucketIfNotExists(String bucketName) {
        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();

        try {
            boolean bucketExists = minioClient.bucketExists(bucketExistsArgs);
            if (!bucketExists) {
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(bucketName)
                                .build());
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return true;
    }

    /**
     * 设置bucket访问策略
     *
     * @return
     */
    @Override
    public void setBucketPolicy(String bucketName, String config) {
        try {
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                    .config(config)
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取文件流
     */
    @Override
    public GetObjectResponse getFile(String objectName) throws IOException {
        GetObjectResponse object = null;
        try {
            object = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(minioPro.getBucket())
                    .object(objectName).build());
            return object;
        } catch (InsufficientDataException e) {
            throw new RuntimeException(e);
        } catch (InternalException e) {
            throw new RuntimeException(e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException(e);
        } catch (InvalidResponseException e) {
            throw new RuntimeException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        } catch (ServerException e) {
            throw new RuntimeException(e);
        } catch (XmlParserException e) {
            throw new RuntimeException(e);
        } catch (ErrorResponseException e) {
            throw new RuntimeException(e);
        }

    }
}
